package UserInterfase;

import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import core.IvoCheckers;
import game.GameEngine;
import java.io.PrintWriter;
import javax.swing.JOptionPane;

/** The graphical representation of the game board.  Basically this had to be
 * moved from the SimpleGUI so that the board would be able to handle its own
 * mouse events.
 */
public class SimpleBoard
	extends JLabel
	implements MouseListener, MouseMotionListener {

	/** The temporary image that gets displayed on to the board. */
	private BufferedImage tempImage;

	/** Image of a light pawn. */
	private BufferedImage lightPawn;

	/** Image of a light king. */
	private BufferedImage lightKing;

	/** Image of a dark pawn. */
	private BufferedImage darkPawn;

	/** Image of a dark king. */
	private BufferedImage darkKing;

	/** The component that acts as the parent of the SimpleBoard. */
	private Component displayParent;

	/** The image that comes from clipping apart of tempImage. */
	private BufferedImage clipping;

	/** The graphics handler of what get displayed on the board. */
	private Graphics2D world;

	/** The piece type that is being moved around by the user's mouse. */
	private BufferedImage piece;

	/** The current "last" position of the sprite on the board. */
	private Point spritePosition;

	/** The location on the board from where the piece originated from. */
	private Point pieceSource;

	/** The location on the board from where the piece is going to. */
	private Point pieceTarget;
        
        private static boolean IsLightColor = true;

        private static PrintWriter mSocketWriter = null;
        private static boolean isNetGame = false;

        public static boolean getIsNetGame() {
            return isNetGame;
        }

        public static void setIsNetGame(boolean isNetGame) {
            SimpleBoard.isNetGame = isNetGame;
        }

        
        public static void setMSocketWriter(PrintWriter mSocketWriter) {
            SimpleBoard.mSocketWriter = mSocketWriter;
        }
        public static PrintWriter getMSocketWriter() {
            return mSocketWriter;
        }
        
        public static boolean getIsLightColor() {
            return IsLightColor;
        }

        public static void setIsLightColor(boolean IsLightColor) {
            SimpleBoard.IsLightColor = IsLightColor;
        }
        
        
	/** Creates the game board for the SimpleGUI. */
	public SimpleBoard(Component parent) {
		// Creates the board itself.
		tempImage = this.createBoard(8);
                
		// Create the pieces.
                if (IsLightColor)
                {
                    lightPawn = this.createPiece(SimpleGUI.startColorLight, SimpleGUI.endColorLight, false);
                    lightKing = this.createPiece(SimpleGUI.startColorLight, SimpleGUI.endColorLight, true);
                    darkPawn = this.createPiece(SimpleGUI.startColorDark, SimpleGUI.endColorDark, false);
                    darkKing = this.createPiece(SimpleGUI.startColorDark, SimpleGUI.endColorDark, true);
                }
                else
                {
                    darkPawn = this.createPiece(SimpleGUI.startColorLight, SimpleGUI.endColorLight, false);
                    darkKing = this.createPiece(SimpleGUI.startColorLight, SimpleGUI.endColorLight, true);
                    lightPawn = this.createPiece(SimpleGUI.startColorDark, SimpleGUI.endColorDark, false);
                    lightKing = this.createPiece(SimpleGUI.startColorDark, SimpleGUI.endColorDark, true);
                }
                
		displayParent = parent;
        
		// Show yourself after resizing, etc...
		ImageIcon fin = new ImageIcon(tempImage);
		this.setSize(fin.getIconWidth(), fin.getIconHeight());
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		Point targetCenter = UITools.getCenterOfComponent(displayParent);
		this.setLocation(UITools.getAdjustToPoint(this, targetCenter));
		updateBoard();

		// Initialize the clipping images... etc.
		world = tempImage.createGraphics();
		clipping = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
		spritePosition = new Point();
	}

	// A whole load of mouse stuff...
	/** Invoked when the mouse enters a component.
	 * @param e the mouse event.
	 */
	public void mousePressed(MouseEvent e) {
                System.out.println("mousePressed");
                if (IvoCheckers.getGame().isBlockMove())
                {
                    return;
                }
		// First check if there is anything to check...
		if (IvoCheckers.getGame() != null) {
                    
			// Game engine and points...
			GameEngine board = IvoCheckers.getGame();
			Point boardCoords = UITools.getGameCoords(e.getPoint(), 50);
			int locX = (int) (boardCoords.getX());
			int locY = (int) (boardCoords.getY());

			// Prepares to grab a piece.
			boolean canGrab = false;
			piece = null;

			// Determine which piece to grab.
			if (board.isDarkPlayerTurn()
				&& (board.isDark(locX, locY))
				&& (board.canMove(locX, locY))) {
				canGrab = true;
				if (board.isKing(locX, locY)) {
					piece = darkKing;
				} else {
					piece = darkPawn;
				}
                                
			} else if (
				board.isLightPlayerTurn()
					&& (board.isLight(locX, locY))
					&& (board.canMove(locX, locY))) {
				canGrab = true;
				if (board.isKing(locX, locY)) {
					piece = lightKing;
				} else {
					piece = lightPawn;
				}
			}

			// Cover the piece's original background...
			if (canGrab) {
				world.setColor(Color.white);
				world.fill(
					new Rectangle((locX - 1) * 50, (locY- 1) * 50, 50, 50));

				pieceSource = new Point(e.getX(), e.getY());
				spritePosition.setLocation(e.getX(), e.getY());
				clipping = this.clipBackground(spritePosition, 50);
				this.spriteMove(spritePosition, e.getPoint());
                                
			}
                        
			updateBoard();

		}
	}

	/** Invoked when the mouse exits a component.
	 * @param e the mouse event.
	 */
	public void mouseReleased(MouseEvent e) {
                System.out.println("mouseReleased");
                boolean turn1 = IvoCheckers.getGame().isDarkPlayerTurn();
                if (IvoCheckers.getGame().isBlockMove())
                {
                    return;
                }
		if (IvoCheckers.getGame() != null && piece != null) {
                        
			// Examine from where the piece is coming from and where to is it going...
			pieceTarget = new Point(e.getX(), e.getY());
			int sourceX =
				(int) ((UITools.getGameCoords(pieceSource, 50)).getX());
			int sourceY =
				(int) ((UITools.getGameCoords(pieceSource, 50)).getY());
			int targetX =
				(int) ((UITools.getGameCoords(pieceTarget, 50)).getX());
			int targetY =
				(int) ((UITools.getGameCoords(pieceTarget, 50)).getY());
                        
			if (IvoCheckers.getGame().isLegalMove(sourceX, sourceY, targetX, targetY)) {
				if (IvoCheckers.getGame().makeMove(sourceX, sourceY, targetX, targetY)) {
                                        
                                        if (getIsNetGame())
                                        {
                                            //3.3:2:4 - move from 3,3 to 2,4
                                            String sendMove = sourceX + "." +  
                                                    sourceY + ":" + targetX + "." + targetY;
                                            mSocketWriter.println(sendMove);
                                            mSocketWriter.flush();
                                            boolean turn2 = IvoCheckers.getGame().isDarkPlayerTurn();
                                            //if player are switched then block game until remote player make move
                                            if (turn1 != turn2)
                                            {
                                                IvoCheckers.getGame().setBlockMove(true);
                                            }
                                        }
					// Do the artwork...
					pieceTarget.setLocation((targetX - 1) * 50, (targetY - 1) * 50);
					this.spriteMove(spritePosition, pieceTarget);
					
					// Delete the jumped opponent.  If it is a jump.
					if(Math.abs(sourceX - targetX) == 2) {
							int changeX = (sourceX - targetX) / 2;
							int changeY = (sourceY - targetY) / 2;
							clearRegion(targetX + changeX, targetY + changeY);		
					}
					
					// Change piece to king?!?!
					if (IvoCheckers.getGame().isKing(targetX, targetY)) {
							clearRegion(targetX, targetY);
							placePieces();
					}
					
					// Check if the game is over.
					if (IvoCheckers.getGame().gameHasBeenWon() == 1) {
                                                JOptionPane.showMessageDialog(
                                                    null,
                                                    "You Lose The Game!",
                                                    "Game Over!",
                                                    JOptionPane.INFORMATION_MESSAGE);
						IvoCheckers.endGame();
					} else if (IvoCheckers.getGame().gameHasBeenWon() == 2) {
                                                 JOptionPane.showMessageDialog(
                                                    null,
                                                    "You Win The Game!",
                                                    "Game Over!",
                                                    JOptionPane.INFORMATION_MESSAGE);
						IvoCheckers.endGame();
					}
					
				} else {
					pieceSource.setLocation((sourceX - 1) * 50, (sourceY - 1) * 50);
					this.spriteMove(spritePosition, pieceSource);
				}
			} else {
				pieceSource.setLocation((sourceX - 1) * 50, (sourceY - 1) * 50);
				this.spriteMove(spritePosition, pieceSource);
			}
			
			updateBoard();
		}
	}

	/** Invoked when a mouse button is pressed on a component and then dragged.
	 * @param e the mouse event.
	 */
	public void mouseDragged(MouseEvent e) {
            //System.out.println("mouseDragged");
		// Already grabbed something...
        if (IvoCheckers.getGame().isBlockMove())
        {
            return;
        }
		if (IvoCheckers.getGame() != null && spritePosition != null && piece != null)
        {
			//this.spriteMove(spritePosition, e.getPoint());
                        //this is for draw 
                        this.spriteMove(spritePosition, new Point(e.getX() - 15,
                                e.getY() - 15));
			updateBoard();
		}
	}

	/**  Invoked when the mouse cursor has been moved onto a component but no 
	 * buttons have been pushed.
	 * @param e the mouse event.
	 */
	public void mouseMoved(MouseEvent e) {
	}

	/** Invoked when the mouse button has been clicked (pressed and released) 
	* on a component.
	* @param e the mouse event.
	*/
	public void mouseClicked(MouseEvent e) {
	}

	/** Invoked when a mouse button has been pressed on a component.
	 * @param e the mouse event.
	 */
	public void mouseEntered(MouseEvent e) {
	}

	/** Invoked when a mouse button has been released on a component.
	 * @param e the mouse event.
	 */
	public void mouseExited(MouseEvent e) {
	}

	/** Creates an image of an alternating checker board.  The board is assumed
	 * to be square.  It uses the default black and white squares.
	 * Requires: size >= 1
	 * 
	 * Mini-TODO: 
	 *  - should add a version of this method that can take different colors.
	 *  - get rid of magic number of 50.
	 * @param size the size of the board in number of squares..
	 * @return an image of a black and white checkerboard.
	 */
	private BufferedImage createBoard(int size) {
		// All the necessary tools for the job... (except for the dwarvish painter :-()
		BufferedImage board =
			new BufferedImage(50 * size, 50 * size, BufferedImage.TYPE_INT_RGB);
		Rectangle square[][] = new Rectangle[size][size];
		Graphics2D boardMaker = board.createGraphics();

		// Algorithm of white/black alternating, based on a sign matrix. 
		for (int row = 1; row <= size; row++) {
			for (int col = 1; col <= size; col++) {

				// Makes the squares... {WARNING: sad oracle method}
				square[row - 1][col - 1] =
					new Rectangle((row - 1) * 50, (col - 1) * 50, 50, 50);
				if (Math.pow(-1, (double) (row + col)) == 1) {
					// Light square...
					boardMaker.setPaint(Color.white);
				} else { // Dark square...
					boardMaker.setPaint(Color.black);
				}
				boardMaker.draw(square[row - 1][col - 1]);
				boardMaker.fill(square[row - 1][col - 1]);
			}
		}

		return board;
	}

	/** Creates a checker piece.  Currently only a plain looking circle, with a nice
	* horizontal gradiant, and king pieces are marked with a bold black crown.
	* Requires = Color <> white or black. (Because same color as board or crown).
	* @param start Color that starts the gradient at the top left corner.
	* @param end Color that ends the gradient at the bottom right corner.
	* @param isKing determines whether or not to attach a crown to the piece.
	* @return image of a circular, ready to use token.
	*/
	private BufferedImage createPiece(Color start, Color end, boolean isKing) {
		BufferedImage piece =
			new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
		Graphics2D token = piece.createGraphics();

		// Poor excuse for no transparency... but it seems to work...
		Rectangle screen = new Rectangle(50, 50, 0, 0);
		token.setPaint(Color.white);
		token.fill(screen);

		// Creates a circle... 
		GradientPaint wow = new GradientPaint(5, 5, start, 45, 45, end);
		token.setPaint(wow);
		token.fillOval(2, 2, 46, 46);

		// Attaches a nice K to the piece.
		if (isKing) {
			GradientPaint kewl =
				new GradientPaint(5, 5, Color.white, 45, 45, Color.black);
                        token.setPaint(kewl);
			Polygon crown = new Polygon();
			crown.addPoint(7, 15);
			crown.addPoint(15, 25);
			crown.addPoint(25, 15);
			crown.addPoint(35, 25);
			crown.addPoint(43, 15);
			crown.addPoint(43, 35);
			crown.addPoint(7, 35);
			token.drawPolygon(crown);
		}

		return piece;
	}

	/** Places the checker pieces in their appropriate locations.  Done when a 
	 * game is started or loaded from memory.  
	 */
	public void placePieces() {

		// THIS WILL CHANGE !!! OR HEADS WILL ROLL ALL OVER THE PLACE !
		GameEngine ugly = IvoCheckers.getGame();

		// Now here comes the uglier, messier part.
		for (int row = 1; row <= ugly.getBoardSize(); row++) {
			int col = 1;
			for (col = 1; col <= ugly.getBoardSize(); col++) {
				if (ugly.isOccupied(row, col)) {

					// Determines what piece is there and draws it there.
					boolean isDark = ugly.isDark(row, col);
					boolean isKing = ugly.isKing(row, col);
					int coordX = (row - 1) * 50;
					int coordY = (col - 1) * 50;
					Image piece;

					if (isDark && isKing) {
						piece = darkKing;
					} else if (isDark && !isKing) {
						piece = darkPawn;
					} else if (!isDark && isKing) {
						piece = lightKing;
					} else {
						piece = lightPawn;
					}

					world.drawImage(piece, coordX, coordY, null);
				}
			}
			col = 1;
		}
	}

	/** Update the board image with the new graphics. */
	public void updateBoard() {
		// Now do me a favor and show yourself... punk!
		ImageIcon fin = new ImageIcon(tempImage);
		this.setIcon(fin);
	}

	/** Moves the sprite to a particular destination. This done by first painting over
	 * sprite's original position with the background.  Then a clip of the new 
	 * background region is taken.  Finally the sprite is pasted on top the 
	 * background image in its new position.
	 * Requires clipping != null and clipBackground() run at least once.
	 * @param source the place where the sprite originates.
	 * @param target the place where the sprite goes to.
	 */
	public void spriteMove(Point source, Point target) {
                System.out.println("spriteMove");
		world.drawImage(
			clipping,
			(int) (source.getX()),
			(int) (source.getY()),
			null);
		clipping = clipBackground(target, 50);

		world.drawImage(
			piece,
			(int) (target.getX()),
			(int) (target.getY()),
			null);
		source.setLocation(target);
	}

	/** Clips the background at a specified location.  The clip is assumed to be a
	 * square.  Note that if the clip's dimension extend over the size of the 
	 * background's image, the clip is resized to fit into the background.
	 * @param origin the top left corner of where to start the clip.
	 * @param size the size of the clip.
	 * @return a clip of the background.
	 */
	private BufferedImage clipBackground(Point origin, int size) {
            System.out.println("clipBackground");
		// This space is for sale.
		int length = size;
		int width = size;

		// If size is to big... then resize.
		if ((size + (origin.getX())) > tempImage.getWidth()) {
			size = tempImage.getWidth() - (int) (origin.getX());
		}
		if ((size + (origin.getY())) > tempImage.getHeight()) {
			size = tempImage.getHeight() - (int) (origin.getY());
		}

		BufferedImage biteMe =
			new BufferedImage(length, width, BufferedImage.TYPE_INT_RGB);

		biteMe.setData(
			(tempImage
				.getSubimage(
					(int) (origin.getX()),
					(int) (origin.getY()),
					size,
					size))
				.getData());
		return biteMe;
	}
	
	/** 
	 * Clears a specified square on the board.  This a dumb method that only
	 * works for an alteranting black/white board.  Use it with caution.
	 * @param boardX the row coordinate of the square to "erase".
	 * @param boardY the column coordinate of the square to "erase".
	 */
	private void clearRegion(int boardX, int boardY) {
		// BOFH Excuse #93:
		//Feature not yet implemented
            System.out.println("clearRegion");
		Rectangle clearer = new Rectangle((boardX - 1) * 50, (boardY - 1) * 50, 50, 50);
		world.setPaint(Color.white);
		world.fill(clearer);
	}
        
        public void repaintBoard()
        {
            
        }
        
        public  void playerPressed(int sourceX, int sourceY) {

            // First check if there is anything to check...
            if (IvoCheckers.getGame() != null) {

                    // Game engine and points...
                    GameEngine board = IvoCheckers.getGame();
                    //Point boardCoords = UITools.getGameCoords(new Point(sourceX, sourceY), 50);
                    //int locX = (int) (boardCoords.getX());
                    //int locY = (int) (boardCoords.getY());
                    int locX = (int) sourceX;
                    int locY = (int) sourceY;

                    // Prepares to grab a piece.
                    boolean canGrab = false;
                    piece = null;

                    // Determine which piece to grab.
                    if (board.isDarkPlayerTurn()
                            && (board.isDark(locX, locY))
                            && (board.canMove(locX, locY))) {
                            canGrab = true;
                            if (board.isKing(locX, locY)) {
                                    piece = darkKing;
                            } else {
                                    piece = darkPawn;
                            }

                    } else if (
                            board.isLightPlayerTurn()
                                    && (board.isLight(locX, locY))
                                    && (board.canMove(locX, locY))) {
                            canGrab = true;
                            if (board.isKing(locX, locY)) {
                                    piece = lightKing;
                            } else {
                                    piece = lightPawn;
                            }
                    }

                    // Cover the piece's original background...
                    if (canGrab) {
                            world.setColor(Color.white);
                            world.fill(
                                    new Rectangle((locX - 1) * 50, (locY- 1) * 50, 50, 50));

                            pieceSource = new Point((sourceX - 1)* 50, (sourceY - 1) * 50);
                            spritePosition.setLocation((sourceX - 1)* 50, (sourceY - 1) * 50);
                            clipping = this.clipBackground(spritePosition, 50);
                            this.spriteMove(spritePosition, new Point((sourceX - 1)* 50, (sourceY - 1) * 50));
                    }

                    updateBoard();

        }
}


	public  void playerReleased(int destinationX, int destinationY) {
                
		if (IvoCheckers.getGame() != null /*&& piece != null*/) {
                        
			// Examine from where the piece is coming from and where to is it going...
			pieceTarget = new Point((destinationX - 1) * 50, (destinationY - 1) * 50);
			int sourceX =
				(int) ((UITools.getGameCoords(pieceSource, 50)).getX());
			int sourceY =
				(int) ((UITools.getGameCoords(pieceSource, 50)).getY());
//			int targetX =
//				(int) ((UITools.getGameCoords(pieceTarget, 50)).getX());
//			int targetY =
//				(int) ((UITools.getGameCoords(pieceTarget, 50)).getY());
                        int targetX = destinationX;
                        int targetY = destinationY;
			
			if (IvoCheckers.getGame().isLegalMove(sourceX, sourceY, targetX, targetY)) {
				if (IvoCheckers.getGame().makeMove(sourceX, sourceY, targetX, targetY)) {
                                        
					// Do the artwork...
					pieceTarget.setLocation((targetX - 1) * 50, (targetY - 1) * 50);
					this.spriteMove(spritePosition, pieceTarget);
					
					// Delete the jumped opponent.  If it is a jump.
					if(Math.abs(sourceX - targetX) == 2) {
							int changeX = (sourceX - targetX) / 2;
							int changeY = (sourceY - targetY) / 2;
							clearRegion(targetX + changeX, targetY + changeY);		
					}
					
					// Change piece to king?!?!
					if (IvoCheckers.getGame().isKing(targetX, targetY)) {
							clearRegion(targetX, targetY);
							placePieces();
					}
					
					// Check if the game is over.
					if (IvoCheckers.getGame().gameHasBeenWon() == 1) {
						IvoCheckers.endGame();
					} else if (IvoCheckers.getGame().gameHasBeenWon() == 2) {
						IvoCheckers.endGame();
					}
					
				} else {
					pieceSource.setLocation((sourceX - 1) * 50, (sourceY - 1) * 50);
					this.spriteMove(spritePosition, pieceSource);
				}
			} else {
				pieceSource.setLocation((sourceX - 1) * 50, (sourceY - 1) * 50);
				this.spriteMove(spritePosition, pieceSource);
			}
			
			updateBoard();
		}
	}
        
        
        
}
